﻿/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/*! @file mainwindow.cpp
  * @brief Implements the MainWindow Class
  * @version 0.4
  * @footnote Encoding Q_OS_WIN: windows-1252 ANSI Latin1; Western European (Windows)
  * @footnote Encoding Q_OS_UNIX: UTF-8
  *
  * @author AK
  * @date 19.06.2012
  *
  * @todo TODO: deCrypt contextmenu entry just on decryptable columns
  */

#include "mainwindow.h"
#include "binaryviewwidget.h"
#include "dbtreeview.h"
#include "decryptdlg.h"
#include "extdevice.h"
#include "extdevicedlg.h"
#include "nss.h"
#include "nssb64.h"
#include "parsethread.h"
#include "pk11pub.h"
#include "pk11sdr.h"
#include "searchdlg.h"
#include "searchthread.h"
#include "sqldlg.h"
#include "sqlitedb.h"
#include "timeconvdlg.h"
#include "version.h"

#include <QSettings>
#include <QFont>
#include <QVector>
#include <QFileDialog>
#include <QMessageBox>
#include <QDateTime>
#include <QList>
#include <QByteArray>
#include <QClipboard>
#include <QDebug>


/*! @brief Konstruktor used if started w/o CLI Argument
 * @callgraph
 * @callergraph
 *
 * @fn MainWindow::MainWindow
 * @param l
 * @param topLeft
 * @param size
 * @param parent
 */
MainWindow::MainWindow (QString l, QPoint topLeft, QSize size, QWidget * parent):
    QMainWindow (parent)
{
    setupUi (this);

    QRect fg;
    fg.setTopLeft(topLeft);
    fg.setSize(size);
    setWindowPositionAndSize(fg);

    prepareSignalSlot ();

// NOTE: depricated in Qt5?
//#ifdef Q_WS_WIN
//    QTextCodec::setCodecForTr(QTextCodec::codecForName("Latin1" /*"Windows-1252"*/));
//#else
//    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
//#endif
//    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    templatePath = QApplication::applicationDirPath () + "/templates";
    if(!QFile(templatePath + "/PPSettings.ini").exists())
        saveTemplate(QDir::homePath(), QDir::homePath());

    currLang = l;
    if(currLang == "de") {
        langGerAct->setChecked(true);
        langEngAct->setChecked(false);
    }
    else {
        langEngAct->setChecked(true);
        langGerAct->setChecked(false);
    }
    timeFormat = "yyyy'-'MM'-'dd";
    doDates = false;
    from = QDate::currentDate ();
    to = QDate::currentDate ();
    filterAction->setEnabled (false);
    dbName = "";
    infoLabel->setText (infoLabel->text ()
                        + " (Version "
                        + QString::number (Version::MAJOR) + '.'
                        + QString::number (Version::MINOR) + '.'
                        + QString::number (Version::REVISION) + '.'
                        + QString::number (Version::BUILD) + ")");

    myStatusBar = this->statusBar ();
    status = new QLabel (tr (" Ready "));
    status->setMinimumSize (status->sizeHint ());
    status->setAlignment (Qt::AlignCenter);
    status->setText (tr ("Ready"));
    status->setToolTip (tr ("Current Status."));
    myStatusBar->addPermanentWidget (status);
    myStatusBar->showMessage (tr ("Done initializing."), 2000);
    dbFilesTreeWidget_0->hideColumn(1);
    cryptButton_0->hide();

}


/**
 * @brief Konstruktor used if started with CLI Argument
 * @callgraph
 * @callergraph
 *
 * @fn MainWindow::MainWindow
 * @param fileName
 * @param l
 * @param topLeft
 * @param size
 * @param parent
 */
MainWindow::MainWindow (QString fileName, QString l, QPoint topLeft, QSize size, QWidget * parent):
    QMainWindow (parent)
{
    setupUi (this);

    QRect fg;
    fg.setTopLeft(topLeft);
    fg.setSize(size);
    setWindowPositionAndSize(fg);

    prepareSignalSlot ();

// NOTE: depricated in Qt5?
//#ifdef Q_OS_WIN
//    QTextCodec::setCodecForTr(QTextCodec::codecForName("Latin1" /*"Windows-1252"*/));
//#else
//    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
//#endif
//    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    currLang = l;
    if(currLang == "de") {
        langGerAct->setChecked(true);
        langEngAct->setChecked(false);
    }
    else {
        langEngAct->setChecked(true);
        langGerAct->setChecked(false);
    }

    templatePath = QApplication::applicationDirPath () + "/templates";
    if(!QFile(templatePath + "/PPSettings.ini").exists())
        saveTemplate(QDir::homePath(), QDir::homePath());

    timeFormat = "yyyy'-'MM'-'dd";
    doDates = false;
    from = QDate::currentDate ();
    to = QDate::currentDate ();
    filterAction->setEnabled (false);
    dbName = fileName;
    infoLabel->setText (infoLabel->text ()
                        + " (Version "
                        + QString::number (Version::MAJOR) + '.'
                        + QString::number (Version::MINOR) + '.'
                        + QString::number (Version::REVISION) + '.'
                        + QString::number (Version::BUILD) + ")");

    myStatusBar = this->statusBar ();
    status = new QLabel (tr (" Ready "));
    status->setMinimumSize (status->sizeHint ());
    status->setAlignment (Qt::AlignCenter);
    status->setText (tr ("Ready"));
    status->setToolTip (tr ("Current Status."));
    myStatusBar->addPermanentWidget (status);

    dbFilesTreeWidget_0->hideColumn(1);
    cryptButton_0->hide();

    openDb (DbDlg::DLG_FILE, DbDlg::DLG_NOFORCE); // DB Name already known

    myStatusBar->showMessage (tr ("Done initializing."), 2000);
}


/**
 * @brief Destructor
 * @callgraph
 * @callergraph
 *
 * @fn MainWindow::~MainWindow
 */
MainWindow::~MainWindow ()
{
    int i = 0;

    // save templates if necessary
    if(dbFilesTreeWidget_0->getSelectedId () == -99)
        qDebug() << "MainWindow\t\t| dtor: No file selected -> no template saved";
    else {
        QString curDir = QDir::toNativeSeparators(sqlitedb.at(dbFilesTreeWidget_0->getSelectedId ())->getPath());
        QString curFile = QDir::toNativeSeparators(sqlitedb.at(dbFilesTreeWidget_0->getSelectedId ())->getLongPath());
        saveTemplate(curDir, curFile);
    }

    saveWindowState();

    // close all open dbs
    Q_FOREACH(SqliteDb *closeme, sqlitedb) {
        sqlite3_close(closeme->handle);
        i++;
    }
    qDebug() << "MainWindow\t\t| closed" << i << "database(s) in dtor";
}

/** @brief sets the members of dbFilesTreeWidgets
 * @callgraph
 * @callergraph
 */
void MainWindow::buildFileTables ()
{
    Q_EMIT resetItemCount();

    // delete tableView_0 entries
    model = new QStandardItemModel (0, 0);
    model->clear ();
    tableView_0->setModel (model);


    // Process all sqliteDb Objects
    for (int count = 0; count < sqlitedb.size (); count++) {
        dbType = sqlitedb.at (count)->getType ();

        // fill data on gui page 0
        dbFilesTreeWidget_0->addFile (count,
                                      sqlitedb.at(count),
                                      sqlitedb.size());
    }
    dbFilesTreeWidget_0->hideColumn(1);
}

/** @brief empties sql edit field on gui page 3
 * @callgraph
 * @callergraph
 */
void MainWindow::clearSqlFieldAct () {
    sqlTextEdit_1->clear ();
}

/**
 * @brief closes binary view tab
 *
 * @fn MainWindow::closeTab
 * @param idx
 */
void MainWindow::closeTab(int idx) {
    tabWidget->removeTab(idx);
    tabWidget->setCurrentIndex(0);
}

/** @brief Copies result Table to QClipboard
 * @callgraph
 * @callergraph
 */
void MainWindow::copy ()
{
    int idx = 0;
    QString file;
    QString copystr;

    QList < QTabBar * >tabList = findChildren < QTabBar * >();
    QTabBar *tabBar = tabList.at (0);
    int currentTab = tabBar->currentIndex ();
    switch (currentTab) {
    case 0:
        idx = dbFilesTreeWidget_0->getSelectedId ();
        break;
    case 1:
        idx = dbFilesTreeWidget_0->getSelectedId ();
        break;
    }

    file = sqlitedb.at (idx)->longpath;


    myStatusBar->showMessage (tr("Exportiere Daten ..."));

    copystr.append (tr("Datei: "));
    copystr.append (file.toUtf8());
    copystr.append (tr("\n"));

    int rows = tableView_0->verticalHeader ()->count ();
    int columns = tableView_0->horizontalHeader ()->count ();

    for (int i = 0; i < columns; i++)
        copystr.append (header.at (i).toUtf8() + '\t');
    copystr.append ('\n');
    for (int i = 0; i < rows; i++) {
        if (i > 0)
            copystr.append ("\n");
        for (int j = 0; j < columns; j++) {
            if (j > 0)
                copystr.append ("\t");
            copystr.append (tableView_0->model ()->
                            data (tableView_0->model ()->index (i, j)).
                            toString ().toUtf8());
        }
    }

    QApplication::clipboard ()->setText (copystr);
    //    outputTextEdit_1->clear();
    log (Qt::blue, tr("Daten in die Zwischenablage exportiert."));
    //    outputTextEdit_1->textCursor().movePosition(QTextCursor::End);
    tableView_0->clearSelection ();
    myStatusBar->showMessage ("Finished exporting!", 2000);
}

/*! @brief Builds sql statment according to selected UI Widgets
 * @callgraph
 * @callergraph
 *
 * @return QString Sql Statement
 */
QString MainWindow::createSqlStmt (QString inTable,
                                   QList<QString> inFields,
                                   const int type)
{
    //QStringList sTerms;
    QList < QString > sTerms;
    QString sql;

    // create FireFox SQL Statments
    //
    if (type == FileType::FT_FIREFOX_PLACES) {
        if (templateCheckBox->checkState() == Qt::Checked)
            sql = ff_sqlHistory;
        else {
            sql = "SELECT " + inFields.at(0);
            for(int i=1; i<inFields.size(); ++i) {
                sql += ",\n ";
                sql += inFields.at(i);
            }
            sql += " FROM ";
            sql += inTable;

            // from and to - set in getSearch()
                    if (doDates) {
                        sql += " \nAND date(date) \nBETWEEN date('" +
                                from.toString (timeFormat) + "')" " AND date('" +
                                to.toString (timeFormat) + "')";
                    }
            if (!searchTerms.simplified ().isEmpty ()) {
                // extract Searchterms
                sTerms = searchTerms.split (",");

                qDebug () << "MainWindow\t\t| createSqlStmt: sTerms.size(): " << sTerms.size ();

                for (int i = 0; i < sTerms.size (); i++) {
                    outputTextEdit_1->append (sTerms.at (i).simplified ());
                    qDebug () << "MainWindow\t\t| createSqlStmt: Search term " << i << ": " << sTerms.at (i).
                                 simplified ();
                    if (i == 0) {
                        sql += "\n WHERE ";
                        sql += searchColumn;
                        sql += " LIKE '%";
                    }
                    else {
                        sql += "\n OR ";
                        sql += searchColumn;
                        sql += " LIKE '%";
                    }
                    sql += sTerms.at (i).simplified () + "%'";
                }
            }
            sql += ";";
        }
    }
    // create Firefox signons.sqlite SQL Statement
    //
    else if (type == FileType::FT_FIREFOX_SIGNONS) {
        if (templateCheckBox->checkState() == Qt::Checked)
            sql = ff_sqlSignons;
        else {
            sql = "SELECT " + inFields.at(0);
            for(int i=1; i<inFields.size(); ++i) {
                sql += ",\n ";
                if(inFields.at(i) == "timeCreated")
                    sql += "(SELECT datetime(moz_logins.timeCreated/1000, 'unixepoch')) AS timeCreated";
                else if(inFields.at(i) == "timeLastUsed")
                    sql += "(SELECT datetime(moz_logins.timeLastUsed/1000, 'unixepoch')) AS timeLastUsed";
                else if(inFields.at(i) == "timePasswordChanged")
                    sql += "(SELECT datetime(moz_logins.timePasswordChanged/1000, 'unixepoch')) AS timePasswordChanged";
                else
                    sql += inFields.at(i);
            }
            sql += " FROM ";
            sql += inTable;
            if (!searchTerms.simplified ().isEmpty ()) {
                // extract Searchterms
                sTerms = searchTerms.split (",");

                qDebug () << "MainWindow\t\t| createSqlStmt: sTerms.size(): " << sTerms.size ();

                for (int i = 0; i < sTerms.size (); i++) {
                    outputTextEdit_1->append (sTerms.at (i).simplified ());
                    qDebug () << "MainWindow\t\t| createSqlStmt: Search term " << i << ": " << sTerms.at (i).
                                 simplified ();
                    if (i == 0) {
                        sql += "\n WHERE ";
                        sql += searchColumn;
                        sql += " LIKE '%";
                    }
                    else {
                        sql += "\n OR ";
                        sql += searchColumn;
                        sql += " LIKE '%";
                    }
                    sql += sTerms.at (i).simplified () + "%'";
                }
            }

            sql += ";";
        }
    }

    // create Android mmssms.db SQL Statements
    //
    else if (type == FileType::FT_ANDROID_MMS) {
        sql = "SELECT datetime(sms.date/1000,'unixepoch') AS date";
        Q_FOREACH (QString field, inFields) {
            sql += ",\n ";
            sql += field;
        }
        sql += "\n FROM sms;";
    }

    // create sql for other DBs
    //
    else if (type == FileType::FT_OTHER) {
        int maxfields = inFields.size();
        sql = "SELECT " + inFields.at(0);
        if(maxfields >1) {
            for(int i=1; i<inFields.size()-1; ++i) {
                sql += ",";
                sql += inFields.at(i);
            }
            sql += "," + inFields.at(maxfields-1);
        }
        sql += " FROM ";
        sql += inTable;
        if (!searchTerms.simplified ().isEmpty ()) {
            // extract Searchterms
            sTerms = searchTerms.split (",");

            qDebug () << "MainWindow\t\t| createSqlStmt: sTerms.size(): " << sTerms.size ();

            for (int i = 0; i < sTerms.size (); i++) {
                outputTextEdit_1->append (sTerms.at (i).simplified ());
                qDebug () << "MainWindow\t\t| createSqlStmt: Search term " << i << ": " << sTerms.at (i).
                             simplified ();
                if (i == 0) {
                    sql += "\n WHERE ";
                    sql += searchColumn;
                    sql += " LIKE '%";
                }
                else {
                    sql += "\n OR ";
                    sql += searchColumn;
                    sql += " LIKE '%";
                }
                sql += sTerms.at (i).simplified () + "%'";
            }
        }

        sql += ";";
    }
    //qDebug () << "MainWindow\t\t| createSqlStmt: SQL: " << sql;
    return sql;
}

/*! @brief shows item text base64 decoded in a decryptdlg
 * if item is clicked in result View
 * @callgraph
 * @callergraph
 * @param keyDir QString: directory containing key3.db, cert8.db
 * @param hostUrl QString: host using username and password
 * @param ciphUName QString: encrypted UserName
 * @param ciphPWord QString: encrypted PassWord
 * @returns replyList QList<QString> cleartext UserName and PassWord
 */
QList<QString> MainWindow::deCrypt (QString keyDir,
                                    QString hostUrl,
                                    QString ciphUName,
                                    QString ciphPWord)
{
    QByteArray ba;
    QString cleartext;
    QString decodetext;
    QString crypttext = ciphUName;
    unsigned int len;
    QString clearUName;
    QString clearPWord;
    QList<QString> replyList;

    //qDebug() << "MainWindow\t\t| deCrypt(): key directory:" << keyDir;

    NSS_Init(keyDir.toLatin1());
    if(!NSS_IsInitialized()) {
        if(ciphUName.at(0) == '~') {
            ciphUName = ciphUName.right(ciphUName.size() - 1);
            if(isBase64(ciphUName.toLatin1().data())) {
                ba = QByteArray::fromBase64(ciphUName.toLatin1());
                clearUName = ba.data();
            }
        }
        else
            clearUName = "Error";

        if(ciphPWord.at(0) == '~') {
            ciphPWord = ciphPWord.right(ciphPWord.size() - 1);
            if(isBase64(ciphPWord.toLatin1().data())) {
                ba = QByteArray::fromBase64(ciphPWord.toLatin1());
                clearPWord = ba.data();
            }
        }
        else
            clearPWord = "Error";

        qDebug() << "MainWindow\t\t| deCrypt(): NSS NOT initialized!";
        replyList << clearUName << clearPWord;
        goto finish;
    }

    SECItem *un_request;
    SECItem *un_reply;
    SECItem *pw_request;
    SECItem *pw_reply;

    // decrypt UserName
    char cipherbuf[1024];
#ifdef Q_OS_UNIX
    strncpy(cipherbuf, ciphUName.toLatin1(), sizeof(cipherbuf) - 1);
#else
    strncpy_s(cipherbuf, ciphUName.toLatin1(), sizeof(cipherbuf) - 1);
#endif
    len = strlen(cipherbuf);
    un_reply = SECITEM_AllocItem(NULL, NULL, 0);  // was 40
    un_request = NSSBase64_DecodeBuffer(NULL, NULL, cipherbuf, len);
    PK11SDR_Decrypt(un_request, un_reply, NULL);
    clearUName = QString::fromLatin1((const char*)un_reply->data, un_reply->len);

    // decrypt PassWord
#ifdef Q_OS_UNIX
    strncpy(cipherbuf, ciphPWord.toLatin1(), sizeof(cipherbuf) - 1);
#else
    strncpy_s(cipherbuf, ciphPWord.toLatin1(), sizeof(cipherbuf) - 1);
#endif
    len = strlen(cipherbuf);
    pw_reply = SECITEM_AllocItem(NULL, NULL, 0);  // was 40
    pw_request = NSSBase64_DecodeBuffer(NULL, NULL, cipherbuf, len);
    PK11SDR_Decrypt(pw_request, pw_reply, NULL);
    clearPWord = QString::fromLatin1((const char*)pw_reply->data, pw_reply->len);

    replyList << clearUName;
    replyList << clearPWord;

    // free Items not used anymore
    SECITEM_FreeItem(un_request, TRUE);
    SECITEM_FreeItem(un_reply, TRUE);
    SECITEM_FreeItem(pw_request, TRUE);
    SECITEM_FreeItem(pw_reply, TRUE);

    NSS_Shutdown();

finish:
    // show Dialog if we have an Url parameter
    if(!hostUrl.isNull()) {
        // show Dialog
        DeCryptDlg *decryptdlg = new DeCryptDlg;
        decryptdlg->decryptLineEdit_1->setText(hostUrl.toUtf8());        // Host line
        decryptdlg->decryptLineEdit_2->setText(clearUName.toUtf8());     // UserName deCrypted
        decryptdlg->decryptLineEdit_3->setText(clearPWord.toUtf8());      // PassWd deCrypted
        decryptdlg->decryptLineEdit_4->setText((const char*)un_request->data);      // UserName deBase64
        decryptdlg->decryptLineEdit_5->setText(QString::fromLatin1((const char*)pw_request->data));      // PassWd deBase64
        decryptdlg->show();
        return replyList;
    }
    // otherwise set UName and PWord field text
    else {
        return replyList;
    }
}

/** @brief called on context menu click
 * @callgraph
 * @callergraph
 */
void MainWindow::deCryptAllAct()
{
    int rows = model->rowCount();
    int cols = model->columnCount();
    int uNameCol = 0;
    int pWordCol = 0;
    QString ciphUname;
    QString ciphPWord;
    QList<QString> replyLst;

    // get significant columns
    for (int i=0; i<cols; i++) {
        if (model->headerData ( i, Qt::Horizontal, Qt::DisplayRole)=="encryptedUsername")
            uNameCol = i;
        if (model->headerData ( i, Qt::Horizontal, Qt::DisplayRole)=="encryptedPassword")
            pWordCol = i;
    }

    // get key directory
    keyDir = sqlitedb.at(dbFilesTreeWidget_0->getSelectedId())->getPath();

    for ( int i=0; i<rows; i++) {
        ciphUname = model->index(i, uNameCol).data().toString();
        ciphPWord = model->index(i, pWordCol).data().toString();
        replyLst = deCrypt(keyDir, NULL, ciphUname, ciphPWord);
        model->item(i, uNameCol)->setText(replyLst.at(0));
        model->item(i, pWordCol)->setText(replyLst.at(1));
    }
    tableView_0->resizeColumnsToContents();
}

/*! @brief Show MessageBox with error code
 * @callgraph
 * @callergraph
 *
 * @param[in] msg QString
 * @param[in] i_txt QString Informative Text
 * @param[in] sql_qry QString Executed SQL Query
 */
void MainWindow::displayError (QString msg, QString i_txt, QString sql_qry)
{

    msgBox.setText (msg);
    msgBox.setInformativeText (tr("Achtung:\n ")
                               + i_txt + tr("\nSonstiges:\n ") + sql_qry);
    msgBox.exec ();
}

/** @brief reacts to SearchDlg
 * @callgraph
 * @callergraph
 */
void MainWindow::getSearch (QString header, bool dD, QString st, QDate f, QDate t)
{
    // set values got from dialog
    searchColumn = header;
    doDates = dD;
    searchTerms = st;
    from = f;
    to = t;
}

/** @brief reacts to SqlDlg
 * @callgraph
 * @callergraph
 */
void MainWindow::getSql (QString stmt)
{
    sqlTextEdit_1->clear ();
    sqlTextEdit_1->appendPlainText (stmt);
}

/** @brief borrowed from mozilla.org source code
 * @callgraph
 * @callergraph
 */
int MainWindow::isBase64(const char *inString)
{
    unsigned int i;
    unsigned char c;

    // base64 table only used to identify the end of a base64 string
    static unsigned char b64[256] = {
        /*  00: */	0,	0,	0,	0,	0,	0,	0,	0,
        /*  08: */	0,	0,	0,	0,	0,	0,	0,	0,
        /*  10: */	0,	0,	0,	0,	0,	0,	0,	0,
        /*  18: */	0,	0,	0,	0,	0,	0,	0,	0,
        /*  20: */	0,	0,	0,	0,	0,	0,	0,	0,
        /*  28: */	0,	0,	0,	1,	0,	0,	0,	1,
        /*  30: */	1,	1,	1,	1,	1,	1,	1,	1,
        /*  38: */	1,	1,	0,	0,	0,	0,	0,	0,
        /*  40: */	0,	1,	1,	1,	1,	1,	1,	1,
        /*  48: */	1,	1,	1,	1,	1,	1,	1,	1,
        /*  50: */	1,	1,	1,	1,	1,	1,	1,	1,
        /*  58: */	1,	1,	1,	0,	0,	0,	0,	0,
        /*  60: */	0,	1,	1,	1,	1,	1,	1,	1,
        /*  68: */	1,	1,	1,	1,	1,	1,	1,	1,
        /*  70: */	1,	1,	1,	1,	1,	1,	1,	1,
        /*  78: */	1,	1,	1,	0,	0,	0,	0,	0,
    };

#define isatobchar(c) (b64[c])
#define MAX_STRING 8192

    for (i = 0; (c = inString[i]) != 0 && isatobchar(c); ++i)
        ;
    if (c == '=') {
        while ((c = inString[++i]) == '=')
            ; /* skip trailing '=' characters */
    }
    if (c && c != '\n' && c != '\r')
        return false;
    if (i == 0 || i % 4)
        return false;
    return true;
}

/**
 * @brief log Text to LogTextBrowser
 * @callgraph
 * @callergraph
 *
 * @param globalcolor Qt::GlobalColor: color of Log Entry
 * @param message const QString: Message to display
 * @return void
 **/
void MainWindow::log (Qt::GlobalColor globalcolor, const QString & message)
{

    outputTextEdit_1->setTextColor (globalcolor);
    outputTextEdit_1->append (message.toUtf8());
    outputTextEdit_1->setTextColor (Qt::black);
}

/** @brief executed when mountExtDevAct Menu clicked()
 * @callgraph
 * @callergraph
 */
void MainWindow::mountExtDeviceAct()
{
    extDevice = new ExtDevice();

    connect (extDevice, SIGNAL(gotDevice(QString)),
             this, SLOT(on_gotDevice(QString)));
    connect (extDevice, SIGNAL(expoReady(QString, QString, QString)),
             this, SLOT(on_expoReady(QString, QString, QString)));
}

/**
 * @brief
 *
 * @fn MainWindow::on_colSelButtonClicked
 */
//void MainWindow::on_colSelButtonClicked() {
//    qDebug() << "MainWindow\t\t| Received on_colSelButtonClicked():";

//    colSelDlg = new ColSelDlg(header);

//    connect (ColSelDlg, SIGNAL(colSelected(QList<QString>)),
//             this, SLOT(t))
//    colSelDlg->show();

//}

/**
 * @brief
 *
 * @fn MainWindow::on_expoReady
 * @param baseDir
 * @param expoDir
 * @param deviceId
 */
void MainWindow::on_expoReady(QString baseDir, QString expoDir, QString deviceId) {
    qDebug() << "MainWindow\t\t| Received expoReady:";
    qDebug() << "MainWindow\t\t| from device:" << deviceId;
    qDebug() << "MainWindow\t\t| at:" << expoDir;

    // if base dir isnt empty -> save last baseDir in settings
    if(!baseDir.isEmpty()) {
        QString templatePath = QApplication::applicationDirPath () + "/templates";
        if(!QFile(templatePath + "/PPSettings.ini").exists()) {
            qDebug() << "MainWindow\t\t| ini file does not exists";
        }
        else {
            QSettings settings (templatePath + "/PPSettings.ini", QSettings::IniFormat);
            settings.beginGroup("LastExpoDir");
            settings.setValue("lastExpoDir", baseDir);
            settings.endGroup();
        }
    }

    dbDir = expoDir;
    dbFilesTreeWidget_0->clearTree ();

    openDb (DbDlg::DLG_FOLDER, DbDlg::DLG_NOFORCE);
}

/**
 * @brief external Device selected
 *
 * @fn MainWindow::on_gotDevice
 * @param dev
 */
void MainWindow::on_gotDevice(QString dev)
{
    qDebug() << "MainWindow\t\t| Received gotDevice:" << dev;
}

/**
 * @brief convert timestamps of right clicked column
 * @callgraph
 * @callergraph
 *
 * @fn MainWindow::on_gotTimeConv
 * @param filetype
 */
void MainWindow::on_gotTimeConv(int filetype, QList<int> selectedColumns)
{
    int col;
    QItemSelectionModel *selectionModel = tableView_0->selectionModel();
    if (selectedColumns.isEmpty())
        col = selectionModel->currentIndex().column();
    else
        col = selectedColumns.at(0);

    uint ts = 0;

    timeConvDlg->progressBar->show();

    // begin iterate over columns
    for (int l=0; l<selectedColumns.size(); l++) {
        col = selectedColumns.at(l);
        if(filetype == FileType::FT_FIREFOX) {
            for(int i=0; i<model->rowCount(); i++) {
                ts = model->item(i, col)->text().toULongLong()/1000000;
                if(ts == 0)
                    model->item(i, col)->setText("");
                else {
                    QDateTime result = QDateTime::fromTime_t(ts);
                    model->item(i, col)->setText(result.toString("yyyy-MM-dd hh:mm:ss"));
                    timeConvDlg->setProgress(i);
                }
            }
        }
        else if(filetype == FileType::FT_ANDROID) {
            for(int i=0; i<model->rowCount(); i++) {
                ts = model->item(i, col)->text().toULongLong()/1000;
                if(ts == 0)
                    model->item(i, col)->setText("");
                else {
                    QDateTime result = QDateTime::fromTime_t(ts);
                    model->item(i, col)->setText(result.toString("yyyy-MM-dd hh:mm:ss"));
                }
            }
        }
        else if(filetype == FileType::FT_CHROME) {
            for(int i=0; i<model->rowCount(); i++) {
                ts = (model->item(i, col)->text().toULongLong()/1000000 - 11644473600);
                if(ts == 0)
                    model->item(i, col)->setText("");
                else {
                    QDateTime result = QDateTime::fromTime_t(ts);
                    model->item(i, col)->setText(result.toString("yyyy-MM-dd hh:mm:ss"));
                }
            }
        }
        else
            qDebug() << "MainWindow\t\t| no time conversion possible";
        tableView_0->resizeColumnToContents(col);
    }
}

/**
 * @brief set to english ui
 * @callgraph
 * @callergraph
 *
 * @fn MainWindow::on_langEngAct_triggered
 */
void MainWindow::on_langEngAct_triggered()
{
    if(lang == "en")
        return;

    QString templatePath = QApplication::applicationDirPath () + "/templates";
    if(!QFile(templatePath + "/PPSettings.ini").exists()) {
        qDebug() << "MainWindow\t\t| ini file does not exists";
    }
    else {
        QSettings settings (templatePath + "/PPSettings.ini", QSettings::IniFormat);
        settings.beginGroup("Language");
        settings.setValue("lang", "en");
        settings.endGroup();
        displayError(tr("Neustart erforderlich"),
                     tr("Die Änderungen werden erst\nnach einem Neustart wirksam"),
                     tr("---"));
    }
}

/**
 * @brief set to german ui
 * @callgraph
 * @callergraph
 *
 * @fn MainWindow::on_langGerAct_triggered
 */
void MainWindow::on_langGerAct_triggered()
{
    if(lang == "de")
        return;

    QString templatePath = QApplication::applicationDirPath () + "/templates";
    if(!QFile(templatePath + "/PPSettings.ini").exists()) {
        qDebug() << "MainWindow\t\t| ini file does not exists";
    }
    else {
        QSettings settings (templatePath + "/PPSettings.ini", QSettings::IniFormat);
        settings.beginGroup("Language");
        settings.setValue("lang", "de");
        settings.endGroup();
        displayError(tr("Neustart erforderlich"),
                     tr("Die Änderungen werden erst\nnach einem Neustart wirksam"),
                     tr("---"));
    }
}

/*! @brief Opens SQLite file
  *
  * @param mode const int
  * @param force const int
  * @return int Return code of sqlite3_open_v2()
  * @todo check return code
  * @sa DbDlg
  */
int MainWindow::openDb (const int mode, const int force)
{
    int id = 0;

    // get last used file
    QSettings settings (templatePath + "/PPSettings.ini", QSettings::IniFormat);
    settings.beginGroup("LastRun");
    QString lastDir = settings.value("lastDir", "").toString();
    settings.endGroup ();
    if(!QDir(lastDir).exists())
        lastDir = QDir::homePath();

    // Process one file
    if (mode == DbDlg::DLG_FILE) {
        if (force == DbDlg::DLG_FORCE) {
            dbName =
                    QFileDialog::getOpenFileName (this, tr("Datenbank öffnen"), lastDir,
                                                  tr("Datenbanken (*.sqlite *.db *.db3);;"
                                                     "Mozilla History (places.sqlite);;"
                                                     "Android MmsSms (mmssms.db);;"
                                                     "Alle Dateien (*.*)"));
        }

        if (dbName.isEmpty ())
            return -99;

        filterAction->setEnabled (true);
        dbLabel_1->setText (QDir::toNativeSeparators(dbName));
        tableView_0->reset ();
        outputTextEdit_1->clear ();

        // put dbName path into allFiles as single member in list
        allFiles.clear ();
        allFiles << dbName;

        status->setText (tr ("OK"));
        status->setToolTip (dbName + tr (" geöffnet!"));

        // create new Databasefile Object
        // because just one file will be processed
        // index is statically 0
        sqlitedb.clear ();
        sqlitedb << (new SqliteDb (allFiles.at (0)));
        //int type = sqlitedb.first ()->getIntType ();
        dbType = sqlitedb.first ()->getType ();
        QString curFile = sqlitedb.first()->getFileName();

        qDebug () << "MainWindow\t\t| Filesize: " << sqlitedb.first ()->getSizeMb () << " MB.";

        // setup UI
        okButton_0->setEnabled (true);
        templateLabel_0->setEnabled (true);
        templateFile = templatePath + "/" + curFile + ".tpl";
        if(QFile(templateFile).exists())
            templateLabel_0->setText(curFile + ".tpl");
        else
            templateLabel_0->setText(curFile + ".tpl nicht vorhanden");

        templatePushButton_0->setEnabled (true);
        myStatusBar->showMessage (dbName + tr(" geöffnet."), 5000);

        // add file to treeView
        buildFileTables ();

        // set selected and expanded
        for(int i=0; i<3; ++i) {
            QModelIndex index = dbFilesTreeWidget_0->model()->index(i, 0).child(0, 0);
            if(index.isValid()) {
                dbFilesTreeWidget_0->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
                dbFilesTreeWidget_0->currentItem()->setExpanded(true);
                openTemplate(dbFilesTreeWidget_0->currentItem(), 0);
            }
        }

        return 0;
    }

    // open directory and process more files
    else if (mode == DbDlg::DLG_FOLDER) {
        if(force == DbDlg::DLG_FORCE) {
        // get last used directory
        QSettings settings (templatePath + "/PPSettings.ini", QSettings::IniFormat);
        settings.beginGroup("LastRun");
        QString lastDir = settings.value("lastDir", "").toString();
        settings.endGroup();
        if(!QDir(lastDir).exists())
            lastDir = QDir::homePath();

        dbDir = QFileDialog::getExistingDirectory (this, tr("Verzeichnis öffnen"),
                                                   lastDir,
                                                   QFileDialog::ShowDirsOnly);
        }
        if (dbDir.isEmpty ())
            return -99;

        tableView_0->reset ();
        outputTextEdit_1->clear ();
        dbLabel_1->setText (tr("Keine Datenbank ausgewählt.") /*QDir::toNativeSeparators(dbDir)*/ );

        allFiles.clear ();
        sqlitedb.clear ();

        // find all files in given dir
        recurseDirectory (QString (dbDir)); // modifies allFiles

        // fill vector of dbs if given file is sqlite file
        for (int i = 0; i < allFiles.size (); i++) {
            temp = new SqliteDb (allFiles.at (i));
            int ft = temp->getIntType();
            if ((ft > 100 && ft < 300) || ft == FileType::FT_OTHER) {
                sqlitedb << temp;
            }
            //if (ft == FileType::FT_OTHER) {
            //    sqlitedb << temp;
            //}
            else if (ft == FileType::FT_INVALID) {
                // do nothing
            }
        }
        if (sqlitedb.isEmpty ()) {
            displayError (tr("Keine Db gefunden. [ID 021]"),
                          tr("Die Suche nach Datenbanken war erfolglos."), "---");
            return -99;
        }

        filterAction->setEnabled (true);
        okButton_0->setEnabled (true);
        status->setText (tr ("OK"));
        status->setToolTip (tr("Alle Sqlite Dateien geöffnet!"));

        // add files to treeView
        buildFileTables ();
        if(dbFilesTreeWidget_0->getSelectedId() == -99)
            displayError (tr("Keine Db ausgewählt. [ID 022]"),
                          tr("Bitte zuerst eine Db auswählen"), "---");

        return 0;
    }

    // refresh query - if okButton pressed
    else if ( mode == DbDlg::DLG_OKBTN ){
        // get the currentTab
        QTabBar *tabBar;
        QList < QTabBar * >tabList = findChildren < QTabBar * >();
        if (!tabList.isEmpty ()) {
            tabBar = tabList.at (0);
        }
        else
            return -99;

        if (tabBar->currentIndex () == 0)
            id = dbFilesTreeWidget_0->getSelectedId ();
        if (tabBar->currentIndex () == 1)
            id = dbFilesTreeWidget_0->getSelectedId ();
        if (id == -99) {
            displayError (tr("Keine Db ausgewählt. [ID 022]"),
                          tr("Bitte zuerst eine Db auswählen"), "---");
            return -99;
        }

        queryDb (sqlitedb.at (id)->handle,
                 sqlitedb.at (id)->getIntType (),
                 sqlitedb.at (id)->getSizeMb ());

        templatePushButton_0->setEnabled (true);
        templateLabel_0->setEnabled (true);
        if(sqlitedb.at(id)->getIntType() == FileType::FT_FIREFOX_SIGNONS) {
            cryptButton_0->show();
            cryptButton_0->setEnabled(true);
        }
        else {
            cryptButton_0->setEnabled(false);
            cryptButton_0->hide();
        }

        return 0;
    }
    else
        return -99;
}

/** @brief executed when okButton clicked()
 * @callgraph
 * @callergraph
 */
void MainWindow::openDbAct ()
{
    openDb (DbDlg::DLG_OKBTN, DbDlg::DLG_NOFORCE /*do not open Dlg*/);
}

/** @brief executed when openButton clicked() or openAction triggered()
 * @callgraph
 * @callergraph
 */
void MainWindow::openFileAct ()
{
    dbFilesTreeWidget_0->clearTree ();
    openDb (DbDlg::DLG_FILE, DbDlg::DLG_FORCE);   // force OpenFileDlg to be shown
}

/** @brief executed when openButton clicked() or openAction triggered()
 * @callgraph
 * @callergraph
 */
void MainWindow::openFolderAct ()
{
    dbFilesTreeWidget_0->clearTree ();
    openDb (DbDlg::DLG_FOLDER, DbDlg::DLG_FORCE); // force OpenDirDlg to be shown
}

void MainWindow::openHexViewAct ()
{
    QWidget *binaryTab = new QWidget(tabWidget);
    QVBoxLayout *mainLayout = new QVBoxLayout;
    QString file = dbFilesTreeWidget_0->getSelectedFileName();
    int idx = tabWidget->addTab(binaryTab, tr("Hex Ansicht von ") + file);

    hexView = new BinaryViewWidget(idx,
                                   dbLabel_1->text(),
                                   binaryTab);
    connect (hexView, SIGNAL(closeMe(int)), this, SLOT(closeTab(int)));
    connect (hexView, SIGNAL(fileOpened()), this, SLOT(parseIt()));
    connect (hexView, SIGNAL(parsingCanceled()), this, SLOT(openHexViewActCanceled()));

    mainLayout->addWidget(hexView);
    binaryTab->setLayout(mainLayout);

    binaryTab->show();
    tabWidget->setCurrentIndex(idx);
    if (hexView->openFile() != 0)
        qDebug() << "MainWindow\t\t| openBinaryView() ERROR opening file.";
}

/**
 * @brief
 *
 * @fn MainWindow::openHexViewActCanceled
 */
void MainWindow::openHexViewActCanceled() {
    int cur = tabWidget->currentIndex();
    tabWidget->setCurrentIndex(0);
    tabWidget->removeTab(cur);
}

/** @brief open template file
 * @callgraph
 * @callergraph
 */
void MainWindow::openTemplate (QTreeWidgetItem * item, int c)
{
    Q_UNUSED(c);

    DbDetails dbdetails = dbFilesTreeWidget_0->getDetailsOfItem (item);

    // if item isnt a dbfile - return
    if (dbdetails.detailsRole != ItemRole::IR_FILE)
        return;

    QString sql;
    QString curFile = QDir::toNativeSeparators(sqlitedb.at(dbFilesTreeWidget_0->getSelectedId ())->getFileName());
    QString table;
    QList<QString> fields;

    templateLabel_0->setEnabled (true);
    templatePushButton_0->setEnabled (true);

    dbLabel_1->setText (QDir::toNativeSeparators(sqlitedb.at (dbdetails.detailsId)->longpath.toUtf8()));

    // if item is a dbfile process further
    templateFile = templatePath + "/" + curFile + ".tpl";
    if (!QFile(templateFile).exists()) {
        templateLabel_0->setText(curFile + ".tpl nicht vorhanden.");
        return;
    }

    QSettings settings (templateFile, QSettings::IniFormat);

    // actually read settings
    if (templateCheckBox->checkState() == Qt::Checked) {
        settings.beginGroup("SqlTempl");
        sql = settings.value("sqltempl", "").toString();
        settings.endGroup();
    }
    else {
    settings.beginGroup ("LastSql");
    sql = settings.value ("sql", "").toString ();
    settings.endGroup ();
    }

    for(int i=1; i<settings.childGroups().size(); ++i) {
        table = settings.childGroups().at(i);
        settings.beginGroup(settings.childGroups().at(i));
        Q_FOREACH(QString j, settings.childKeys())
            fields.append(j);
        settings.endGroup();
        dbFilesTreeWidget_0->setCheckedItems(table, fields);
        fields.clear();
    }

    //dbFilesTreeWidget_0->setCheckedItems (lst);

    templateLabel_0->setText(curFile + ".tpl");
    sqlTextEdit_1->clear ();
    sqlTextEdit_1->appendPlainText (QString (sql));
}

/**
 * @brief parse file
 *
 * @fn MainWindow::parseIt
 */
void MainWindow::parseIt() {
    qDebug() << "MainWindow\t\t| parseIt() entered.";

    Q_EMIT hexView->parseFile();
}

/** @brief includes all the Signal/Slot connections
 * @callgraph
 * @callergraph
 */
void MainWindow::prepareSignalSlot ()
{
    qRegisterMetaType < ResultSet > ();
    tableView_0->setContextMenuPolicy(Qt::CustomContextMenu);

    // Menu Actions
    connect (fileOpenAction, SIGNAL (triggered()),
             this, SLOT (openFileAct ()));
    connect (folderOpenAction, SIGNAL (triggered ()),
             this, SLOT (openFolderAct ()));
    connect (extDeviceOpenAction, SIGNAL(triggered ()),
             this, SLOT (mountExtDeviceAct()));
    connect (hexOpenAction, SIGNAL (triggered()),
             this, SLOT(openHexViewAct()));
    connect (sqlStmtAction, SIGNAL (triggered ()),
             this, SLOT (showSqlWindow ()));
    connect (filterAction, SIGNAL (triggered ()),
             this, SLOT (showSearchWindow ()));
    connect (aboutQtAction, SIGNAL (triggered ()),
             qApp, SLOT (aboutQt ()));

    // Button clicks
    connect (okButton_0, SIGNAL (clicked ()), this, SLOT (openDbAct ()));
    connect (copyButton_0, SIGNAL (clicked ()), this, SLOT (copy ()));
    connect (templatePushButton_0, SIGNAL (clicked ()),
             this, SLOT (saveTemplate ()));
    connect (runQueryButton_1, SIGNAL (clicked ()), this, SLOT (queryDbAct ()));
    connect (deleteQueryButton_1, SIGNAL (clicked ()),
             this, SLOT (clearSqlFieldAct ()));
    connect (cryptButton_0, SIGNAL (clicked ()),
             this, SLOT (deCryptAllAct ()));

    // other actions
    connect (this, SIGNAL(resetItemCount()),
             dbFilesTreeWidget_0, SLOT(_on_resetItemCount()));
    connect (dbFilesTreeWidget_0, SIGNAL (itemClicked (QTreeWidgetItem *, int)),
             this, SLOT (openTemplate (QTreeWidgetItem *, int)));
    connect (dbFilesTreeWidget_0, SIGNAL (dbChanged (QTreeWidgetItem *, int)),
             this, SLOT (openTemplate (QTreeWidgetItem *, int)));
    connect (dbFilesTreeWidget_0, SIGNAL(logMe(Qt::GlobalColor,QString)),
             this, SLOT(log(Qt::GlobalColor,QString)));
    connect (tableView_0, SIGNAL ( customContextMenuRequested(const QPoint &)),
             this, SLOT(showContextMenu(const QPoint &)));
}

/*! @brief Runs Query against SQLite DB
  * @param tmp sqlite3*: handle to dbfile
  * @param type const int:  FT_FIREFOX=0, FT_ANDROID=1, ...
  * @param size const int: Size of Data to be searched
  * @return int: Return code of sqlite3_get_table()
  */
int MainWindow::queryDb (sqlite3 * tmp, const int type, const double size)
{
    // check if file is selected
    int id = dbFilesTreeWidget_0->getSelectedId ();
    if (id == -99) {
        displayError (tr("Keine Db ausgewählt. [ID 022]"),
                      tr("Bitte zuerst eine Db auswählen"), "---");
    return -99;
    }

    myStatusBar->showMessage (tr("Suche wird ausgeführt!"));

    QString sql;

    header.clear ();              // for gui

    QString table = dbFilesTreeWidget_0->getTable();
    QList<QString> fields = dbFilesTreeWidget_0->getCheckedItemNames ();

    // create sql statement
    sql = createSqlStmt (table, fields, type);
    if (sql.isEmpty ()) {
        displayError (tr("SQL Suchstring ist leer. [ID 032]"),
                      tr("Zuerst tables auswählen"), "-");
        return -99;
    }

    sqlTextEdit_1->clear ();
    sqlTextEdit_1->appendPlainText (sql);

    // start query thread
    QString msg = QString (tr("Durchsuche %1.2f MB an Daten ...")).arg (size).toUtf8();
    myStatusBar->showMessage (msg);

    SearchThread *query = new SearchThread (tmp, sql,
                                            size, 0);

    connect (query, SIGNAL (started ()), this, SLOT (queryStarted ()));
    connect (query, SIGNAL (finished (ResultSet)),
             this, SLOT (queryFinished (ResultSet)));
    connect (query, SIGNAL (terminated ()), query, SLOT (deleteLater ()));
    query->start ();

    return 0;
}

/** @brief executed when runQueryButton_1 on GUI Page 1 clicked()
 * @callgraph
 * @callergraph
 */
void MainWindow::queryDbAct ()
{
    myStatusBar->showMessage (tr("Suche wird ausgeführt!"));

    QString sql;
    int idx = dbFilesTreeWidget_0->getSelectedId ();
    if (idx == -99) {
        displayError (tr("Keine Db ausgewählt. [ID 011]"),
                      tr("Bitte zuerst eine Db auswählen"), "-");
        return;
    }

    sql = sqlTextEdit_1->toPlainText ();
    if (sql.isEmpty ()) {
        displayError (tr("Keine Suche vorhanden! [ID 012]"),
                      tr("Bitte Suche eingeben"), tr("-"));
        return;
    }

    // start thread
    double size = sqlitedb.at (idx)->getSizeMb ();
    QString msg = QString (tr("Durchsuche %1 MB an Daten ...")).arg (size).toUtf8();
    myStatusBar->showMessage (msg);

    SearchThread *query = new SearchThread (sqlitedb.at (idx)->handle,
                                            sql,
                                            size,
                                            1);

    connect (query, SIGNAL (started ()), this, SLOT (queryStarted ()));
    connect (query, SIGNAL (finished (ResultSet)),
             this, SLOT (queryFinished (ResultSet)));
    connect (query, SIGNAL (terminated ()), query, SLOT (deleteLater ()));
    query->start ();

    // now wait for on_QueryFinished(ResultSet)
}


/** @brief query thread finished
 * @callgraph
 * @callergraph
 */
int MainWindow::queryFinished (ResultSet res)
{
    myStatusBar->showMessage (tr("Suchthread beendet."), 5000);

    resultset = res;

    qDebug () << "MainWindow\t\t| Thread finished! resultset.rc =" << QString::
                 number (resultset.rc);
    qDebug () << "MainWindow\t\t| Thread finished! resultset.rows =" << QString::
                 number (resultset.rows);

    myStatusBar->showMessage (tr("Durchsuchen und Ergebnisliste aufbauen."), 5000);

    // Process results if called from GUI page 'Datenbanken'
    if (resultset.caller == 0) {
        if (resultset.rc) {
            displayError (QString
                          (tr("Suche konnte nicht ausgeführt werden! [ID 033]")),
                          QString (tr("ID 033" /*sqlite3_errmsg(db) */ )),
                          QString (resultset.sql));
            return resultset.rc;
        }
        // process results
        else {
            if (resultset.rows < 1) {
                displayError (QString (tr("Keine Einträge gefunden. [ID 034]")),
                              QString (tr("Keine Einträge gefunden.")),
                              QString (tr("-")));
                tabWidget->setCurrentIndex (2);
                return -99;
            }
            status->setText (QString::number (resultset.rows) +
                             tr(" Datensätze gefunden!"));
            pd = new QProgressDialog (QString ("Durchsuche %1 MB an Daten.").arg (resultset.size, 0, 'd', 2).toUtf8(),
                                      "Beenden", 0,
                                      resultset.rows);
            if (resultset.rows > 1)
                pd->show ();
            // connect(pd, SIGNAL(canceled()), this, SLOT(cancel()));

            // create result TableViews
            model = new QStandardItemModel (resultset.rows, resultset.columns);
            model->clear ();
            tableView_0->setModel (model);

            // set Column Names
            for (int c = 0; c < resultset.columns; c++) {
                header << QString (resultset.results[c]).toUtf8();
                model->setHorizontalHeaderLabels (header);
            }
            // build Data Section of tableView
            for (int r = 1; r <= resultset.rows; r++) {
                for (int c = 0; c < resultset.columns; c++) {
                    int cellPos = (r * resultset.columns) + c;
                    QStandardItem *item =
                            new QStandardItem (QString (resultset.results[cellPos]));
                    model->setItem (r - 1, c, item);
                }
                pd->setValue (r); // update ProgressDialog
            }
            //tableView_0->resizeColumnsToContents ();
        }
        tableView_0->resizeColumnsToContents ();

        sqlite3_free_table (resultset.results);
        myStatusBar->showMessage (tr("Suche beendet!"), 5000);
    }
    // Process results if called from GUI page 'SQL/Log'
    else if (resultset.caller == 1) {
        if (resultset.rc) {
            displayError (tr("Suche konnte nicht ausgeführt werden! [ID 013]"),
                          QString (sqlite3_errmsg (db)),
                          QString (resultset.sql));
            return resultset.rc;
        } else {
            QString outbuf;

            status->setText (QString::number (resultset.rows) +
                             tr(" Datensätze gefunden!"));
            pd = new QProgressDialog (tr("Durchsuche %1 MB an Daten."),
                                      "Beenden", 0, resultset.rows);
            // connect(pd, SIGNAL(canceled()), this, SLOT(cancel()));

            // set Column Names
            for (int c = 0; c < resultset.columns; c++) {
                outbuf.append (QString (resultset.results[c]) + "\t").toUtf8();
            }
            outbuf.append (tr("\n---------------\n"));

            // build Data Section of outputTextEdit_1
            for (int r = 1; r <= resultset.rows; r++) {
                for (int c = 0; c < resultset.columns; c++) {
                    int cellPos = (r * resultset.columns) + c;
                    outbuf.append (resultset.results[cellPos]);
                    outbuf.append ("\t");
                }
                outbuf.append ("\n");
                pd->setValue (r); // update ProgressDialog
            }
            outbuf.append ("---------------\n");
            outbuf.append (QString::number (resultset.rows) +
                           tr(" Datensätze gefunden."));

            //outputTextEdit_1->clear();
            outputTextEdit_1->append ("----------");
            outputTextEdit_1->setText (outbuf);
            outputTextEdit_1->append ("-----END-----\n");
            QTextCursor c = outputTextEdit_1->textCursor ();
            c.movePosition (QTextCursor::End);
            outputTextEdit_1->setTextCursor (c);
        }

        sqlite3_free_table (resultset.results);
        myStatusBar->showMessage (tr("Suche beendet!"), 5000);
        return 0;
    }
    // should not reach this
    return -99;
}

/** @brief called on thread started
 * @callgraph
 * @callergraph
 */
void MainWindow::queryStarted ()
{
    myStatusBar->showMessage (tr("Suchthread gestartet. Bitte warten ..."));
    qDebug () << "MainWindow\t\t| Thread started ...";
}

/*! @brief scans a given Directory sDir for sqlite files
 * @callgraph
 * @callergraph
 *
 * @param sDir const QString directory to be scanned
 * ! modifies global Variable allFiles !
 */
void MainWindow::recurseDirectory (const QString & sDir)
{
    //    QStringList all;
    QDir dir (sDir);
    QFileInfoList list = dir.entryInfoList ();
    for (int iList = 0; iList < list.count (); iList++) {
        QFileInfo info = list[iList];

        QString sFilePath = info.filePath ();
        if (info.isDir ()) {
            // recursive
            if (info.fileName () != ".." && info.fileName () != ".") {
                recurseDirectory (sFilePath);
            }
        } else {
            allFiles << QString (sFilePath);
        }
    }
}

/** @brief context menu convert
 * @callgraph
 * @callergraph
 */
void MainWindow::rightBtnConvertAct()
{
    QItemSelectionModel *selectionModel = tableView_0->selectionModel();
    int col = selectionModel->currentIndex().column();
    int row = selectionModel->currentIndex().row();
    int maxprogress = tableView_0->model()->rowCount();
    QString ts = model->item(row, col)->text();

    timeConvDlg = new TimeConvDlg(ts, maxprogress, header);
    connect (timeConvDlg, SIGNAL(gotTimeConv(int, QList<int>)),
            this, SLOT(on_gotTimeConv(int, QList<int>)));

    timeConvDlg->show();
}

/** @brief context menu copy
 * @callgraph
 * @callergraph
 */
void MainWindow::rightBtnCopyAct()
{
    QItemSelectionModel *selectionModel = tableView_0->selectionModel();
    QString selText = selectionModel->currentIndex().data().toString();
    QApplication::clipboard ()->setText (selText);

    qDebug() << "MainWindow\t\t| selection copied to clipboard:" << selText;
}

/** @brief context menu deCrypt
 * @callgraph
 * @callergraph
 */
void MainWindow::rightBtnDeCryptAct()
{
    QItemSelectionModel *selectionModel = tableView_0->selectionModel();
    QString hostUrl;
    QString ciphUName;
    QString ciphPWord;

    // get significant columns
    for (int i=0; i<model->columnCount(); i++) {
        if (model->headerData ( i, Qt::Horizontal, Qt::DisplayRole)=="hostname") {
            hostUrl = model->index(selectionModel->currentIndex().row(), i).data().toString();
            qDebug() << "MainWindow\t\t| hostUrl:" << hostUrl;
        }
        if (model->headerData ( i, Qt::Horizontal, Qt::DisplayRole)=="encryptedUsername") {
            ciphUName = model->index(selectionModel->currentIndex().row(), i).data().toString();
            qDebug() << "MainWindow\t\t| ciphUName:" << ciphUName;
        }
        if (model->headerData ( i, Qt::Horizontal, Qt::DisplayRole)=="encryptedPassword") {
            ciphPWord = model->index(selectionModel->currentIndex().row(), i).data().toString();
            qDebug() << "MainWindow\t\t| ciphPWord:" << ciphPWord;
        }
    }

    selectionModel = dbFilesTreeWidget_0->selectionModel();
    keyDir = sqlitedb.at(dbFilesTreeWidget_0->getSelectedId())->getPath();

    deCrypt(keyDir, hostUrl, ciphUName, ciphPWord);
}

/** @brief context menu filter
 * @callgraph
 * @callergraph
 */
void MainWindow::rightBtnFilterAct()
{
    QItemSelectionModel *selectionModel = tableView_0->selectionModel();
    QString header;
    int col = 0;

    col = selectionModel->currentIndex().column();
    header = model->headerData (col, Qt::Horizontal, Qt::DisplayRole).toString();
    qDebug() << "MainWindow\t\t| header:" << header;

    int idx = dbFilesTreeWidget_0->getSelectedId ();
    from = sqlitedb.at (idx)->first_entry;
    to = sqlitedb.at (idx)->last_entry;

    SearchDlg *searchdialog = new SearchDlg (header, searchTerms, from, to);
    connect (searchdialog, SIGNAL (accepted (QString, bool, QString, QDate, QDate)),
             this, SLOT (getSearch (QString, bool, QString, QDate, QDate)));

    searchdialog->show();
}

/** @brief context menu open Url
 * @callgraph
 * @callergraph
 */
void MainWindow::rightBtnOpenUrlAct()
{
    QItemSelectionModel *selectionModel = tableView_0->selectionModel();
    QUrl url = selectionModel->currentIndex().data().toString();
    bool success = false;

    if(url.isValid())
        success = QDesktopServices::openUrl(url);
    if(success)
        qDebug() << url << "opened in Standardbrowser";
    else
        displayError(tr("Fehler"), tr("Kann die URL nicht öffnen"), url.toString());
}

/*! @brief save settings to template
 *  overloaded function
 * @callgraph
 * @callergraph
 */
void MainWindow::saveTemplate ()
{
    int ft;
    QString curFile = QDir::toNativeSeparators(sqlitedb.at(dbFilesTreeWidget_0->getSelectedId ())->getFileName());
    QList<QStringList> sets = dbFilesTreeWidget_0->getSettings();

    if (!QDir (templatePath).exists ()) {
        QDir ().mkdir (templatePath);
        qDebug () << "MainWindow\t\t| Folder " + templatePath + " created.";
    }

    if (dbFilesTreeWidget_0->getSelectedId () != -99)
        ft = sqlitedb.at (dbFilesTreeWidget_0->getSelectedId ())->getIntType ();
    else
        ft = FileType::FT_OTHER;

    QString sqlbuf = createSqlStmt (dbFilesTreeWidget_0->getTable(),
                                    dbFilesTreeWidget_0->getCheckedItemNames (),
                                    ft);

    // set Settings Path and Name
    templateFile = templatePath + "/" + curFile + ".tpl";

    // insert Values
    if (QFile (templateFile).exists ())
        QFile (templateFile).remove ();

    QSettings settings (templateFile, QSettings::IniFormat);

    settings.beginGroup("SqlTemplate");
    if(ft == FileType::FT_FIREFOX_PLACES)
        settings.setValue("sqltempl", ff_sqlHistory);
    else if (ft == FileType::FT_FIREFOX_SIGNONS)
        settings.setValue("sqltempl", ff_sqlSignons);
    settings.endGroup();

    settings.beginGroup ("LastSql");
    settings.setValue ("sql", sqlbuf);
    settings.endGroup ();

    for(int i=0; i<sets.size(); ++i) {
        settings.beginGroup (sets.at(i).at(0));
        for(int j=1; j<sets.at(i).size(); ++j) {
            settings.setValue (sets.at(i).at(j), true);
        }
        settings.endGroup ();
    }

    templateLabel_0->setText(curFile + tr(".tpl gespeichert."));
    myStatusBar->showMessage ("Template: " + templateFile + " written", 5000);
}

/**
 * @brief saves App Settings
 * overloaded function
 * @callgraph
 * @callergraph
 * @fn MainWindow::saveTemplate
 * @param dir QString: current directory to be saved
 */
void MainWindow::saveTemplate(QString dir,
                              QString file) {
    if (!QDir (templatePath).exists ()) {
        QDir ().mkdir (templatePath);
        qDebug () << "MainWindow\t\t| Folder " + templatePath + " created.";
    }
    templateFile = templatePath + "/PPSettings.ini";


    // insert Values
    QSettings settings (templateFile, QSettings::IniFormat);
    settings.beginGroup ("LastRun");
    settings.setValue ("lastDir", dir);
    settings.setValue("lastFile", file);
    settings.endGroup ();
}

/**
 * @brief saves App Settings
 *
 * @callgraph
 * @callergraph
 * @fn MainWindow::saveTemplate
 * @param dir QString: current directory to be saved
 */
void MainWindow::saveWindowState() {
    if (!QDir (templatePath).exists ()) {
        QDir ().mkdir (templatePath);
        qDebug () << "MainWindow\t\t| Folder " + templatePath + " created.";
    }
    templateFile = templatePath + "/PPSettings.ini";

    qDebug() << "MainWindow\t\t| saveTemplate(): topLeft, size:" << pos() << size();


    // insert Values
    QSettings settings (templateFile, QSettings::IniFormat);
    settings.beginGroup ("LastRun");
    settings.setValue("winPos", pos());
    settings.setValue("winSize", size());
    settings.endGroup ();
}


/** @brief show context menu
 * @callgraph
 * @callergraph
 */
void MainWindow::showContextMenu(const QPoint & pos) {
    Q_UNUSED(pos);

    QMenu contextMenu(this);

    contextMenu.addAction ( tr("diese Spalte filtern"),
                            this, SLOT (rightBtnFilterAct()) );
    contextMenu.addAction ( tr("Zeitstempel konvertieren"),
                            this, SLOT (rightBtnConvertAct()) );
    contextMenu.addAction ( tr("in die Zwischenablage kopieren"),
                            this, SLOT (rightBtnCopyAct()) );
    contextMenu.addAction ( tr("Url im Browser öffnen"),
                            this, SLOT (rightBtnOpenUrlAct()) );
    contextMenu.addAction ( tr("deCrypt"),
                            this, SLOT (rightBtnDeCryptAct()) );
    contextMenu.exec( QCursor::pos() );
}

/** @brief shows SearchDlg Window, if triggered from MenuAction
 * @callgraph
 * @callergraph
 */
void MainWindow::showSearchWindow () {
    int idx = 0;
    from = QDate::currentDate ();
    to = QDate::currentDate ();

    QList < QTabBar * >tabList = findChildren < QTabBar * >();
    QTabBar *tabBar = tabList.at (0);
    int currentTab = tabBar->currentIndex ();
    switch (currentTab) {
    case 0:
        idx = dbFilesTreeWidget_0->getSelectedId ();
        break;
    case 1:
        idx = dbFilesTreeWidget_0->getSelectedId ();
        break;
    }

    if (idx == -99) {
        displayError (tr("Keine Datenbank ausgewählt! [ID 050]"),
                      tr("Zuerst eine Datenbank auswählen"),
                      tr("---"));
        return;
    }

    from = sqlitedb.at (idx)->first_entry;
    to = sqlitedb.at (idx)->last_entry;

    SearchDlg *searchdialog = new SearchDlg (NULL, searchTerms, from, to);
    connect (searchdialog, SIGNAL (accepted (QString, bool, QString, QDate, QDate)),
             this, SLOT (getSearch (QString, bool, QString, QDate, QDate)));

    searchdialog->show ();
}

/** @brief shows SqlDlg Window, if triggered from MenuAction
 * @callgraph
 * @callergraph
 */
void MainWindow::showSqlWindow () {
    SqlDlg *dialog = new SqlDlg;
    connect (dialog, SIGNAL (okButtonClicked (QString)),
             this, SLOT (getSql (QString)));
    dialog->show ();
}

